<!-- 表格组件 -->
<template>
  <div class="pi-table">
    <!-- 表格的搜索表单 -->
    <SearchForm
      v-if="searchFormList.length"
      :size="$attrs.size"
      :searchFormList
      @onSearch="
        (value) => {
          emit('onSearch', value);
        }
      "
    />

    <!--表头按钮、搜索-->
    <div class="table-header">
      <div class="table-header-left">
        <slot name="tableHeader" />
      </div>
      <el-input
        v-if="isSearch"
        v-model.trim="data.searchValue"
        :placeholder="$t('基础文本.请搜索')"
        :size="$attrs.size"
        :suffix-icon="Search"
        @change="searchTable"
        clearable
      />
    </div>

    <!--表格主体-->
    <div class="table-main" v-loading="loading">
      <el-table
        ref="tableRef"
        v-bind="$attrs"
        :data="data.showTableData"
        @selection-change="handleSelectionChange"
      >
        <template v-for="item in tableColumns" :key="item">
          <!-- 默认插槽 -->
          <slot />
          <!--单选框/复选框/展开列/序号/拖拽-->
          <el-table-column v-if="item.type" v-bind="item" :width="item.width || 54">
            <template #default="scope">
              <!-- expand -->
              <template v-if="item.type === 'expand'">
                <slot :name="item.type" v-bind="scope" />
              </template>
              <!-- drag -->
              <el-tag v-if="item.type === 'drag'" class="move" size="small">
                <el-icon><Rank /></el-icon>
              </el-tag>
              <!-- radio。需要传入rowKey，唯一值，如id、编号 -->
              <el-radio
                v-if="item.type === 'radio'"
                v-model="data.radio"
                :label="scope.row[$attrs.rowKey]"
                class="column-radio"
                @change="emit('radioChange', scope.row)"
              >
                <i></i>
              </el-radio>
            </template>
          </el-table-column>
          <!--其他列-->
          <el-table-column v-else show-overflow-tooltip v-bind="item">
            <template #header="scope">
              <!-- 自定表头插槽 -->
              <slot :name="'header_' + item.prop" v-bind="scope">
                <p class="header-cell">{{ scope.column.label }}</p>
              </slot>
              <!-- 表头筛选 -->
              <template v-if="item.filter">
                <el-popover placement="bottom-start" :width="260" :hide-after="10" trigger="click">
                  <template #reference>
                    <el-link class="filter-icon" :underline="false" @click.stop>
                      <el-icon size="12"><ArrowDownBold /></el-icon>
                      <!--<el-icon size="12"><Filter /></el-icon>-->
                    </el-link>
                  </template>
                  <div class="table-filter-box">
                    <div class="filter-top">
                      <div class="sub-title">{{ $t('组件文本.快捷操作') }}</div>
                      <el-link
                        class="block-btn"
                        :class="{ 'active-color': scope.column.activeSort === 1 }"
                        :underline="false"
                        @click="onSort(scope.column, 'ascending', true)"
                      >
                        <el-icon><SortUp /></el-icon>
                        {{ $t('组件文本.升序排列') }}
                      </el-link>
                      <el-link
                        :class="{ 'active-color': scope.column.activeSort === 2 }"
                        :underline="false"
                        @click="onSort(scope.column, 'descending', true)"
                      >
                        <el-icon><SortDown /></el-icon>
                        {{ $t('组件文本.降序排列') }}
                      </el-link>
                    </div>
                    <div class="filter-bottom">
                      <div class="sub-title">
                        {{ $t('组件文本.筛选') }}{{ $t('组件文本.条件') }}
                      </div>
                      <div class="sub-section">
                        {{ scope.column.label }}{{ $t('组件文本.筛选') }}
                      </div>
                      <!-- 选择 -->
                      <el-select
                        v-if="item.filter === 'select'"
                        v-model="scope.column.filteredValue"
                        :teleported="false"
                        multiple
                        @change="changeFilter(scope.column, item, item.filter)"
                      >
                        <el-option
                          v-for="(val, index) in item.filterList"
                          :key="index"
                          :value="val"
                          :label="val"
                        />
                      </el-select>
                      <!-- 时间 -->
                      <el-date-picker
                        v-else-if="item.filter === 'date'"
                        type="daterange"
                        :start-placeholder="$t('基础文本.请选择')"
                        :end-placeholder="$t('基础文本.请选择')"
                        style="width: 90%"
                        :teleported="false"
                        v-model="scope.column.filteredValue"
                        @change="changeFilter(scope.column, item, item.filter)"
                      />
                      <!-- input -->
                      <el-input
                        v-else-if="item.filter === 'input'"
                        :placeholder="$t('基础文本.请输入') + item.label"
                        v-model="scope.column.filteredValue"
                        clearable
                        @input="inputFilter(scope.column, item, item.filter)"
                      />
                    </div>
                  </div>
                </el-popover>
              </template>
            </template>
            <template #default="scope">
              <!--需自定义列-->
              <slot :name="item.prop" v-bind="scope">
                {{ scope.row[item.prop] }}
              </slot>
            </template>
          </el-table-column>
        </template>
        <!--操作列-->
        <el-table-column
          v-if="slots.operation"
          :label="$t('组件文本.操作')"
          prop="operation"
          fixed="right"
          width="120"
        >
          <template #default="scope">
            <slot name="operation" v-bind="scope" />
          </template>
        </el-table-column>
        <!-- 设置按钮列 -->
        <el-table-column
          v-if="tableColumns.length && settingColumns.getColumns"
          align="right"
          fixed="right"
          width="40"
        >
          <template #header>
            <el-link
              style="font-size: 14px"
              :underline="false"
              @click="data.showColumnSetting = true"
            >
              <el-icon><Setting /></el-icon>
            </el-link>
          </template>
        </el-table-column>
        <!-- 插入表格最后一行之后的插槽 -->
        <template #append>
          <slot name="append" />
        </template>
        <!-- 无数据 -->
        <template #empty>
          <div class="table-empty">
            <slot name="empty">
              <el-empty :description="$t('组件文本.暂无数据')" />
            </slot>
          </div>
        </template>
      </el-table>

      <!--分页-->
      <pagination
        v-if="isPagination && pageable.total"
        :tableColumns
        :pagination="pageable"
        :selectionList="data.selectionList"
        @sizeChange="sizeChange"
        @currentChange="currentChange"
      />
    </div>
  </div>

  <!--列设置-->
  <columnSetting
    v-if="settingColumns.getColumns"
    v-model:isShow="data.showColumnSetting"
    :settingColumns
    @confirmSetting="confirmSetting"
  />
</template>

<script setup name="piTable">
import { Search } from '@element-plus/icons-vue';
import Sortable from 'sortablejs';
import SearchForm from './components/searchForm.vue';
import Pagination from './components/pagination.vue';
import ColumnSetting from './components/columnSetting.vue';

let timer = null; // 维护一个 timer
let searchTableData = []; //搜索框搜索出来的数据（前端分页用）
const tableRef = ref(null); //表格ref
const slots = useSlots();

const props = defineProps({
  //加载状态
  loading: {
    type: Boolean,
    default: false,
  },
  //是否需要搜索
  isSearch: {
    type: Boolean,
    default: false,
  },
  //是否需要分页
  isPagination: {
    type: Boolean,
    default: true,
  },
  //获取表头数据的接口
  settingColumns: {
    type: Object,
    default: () => {
      return {
        getColumns: null, //获取表头接口
        getColumnsParams: {}, //接口入参
        setColumns: null, //保存设置的表头接口
        setColumnsParams: {}, //接口入参
      };
    },
  },
  //获取表格数据的接口
  // requestTable: {
  //   type: Object,
  //   default: () => {
  //     return {
  //       api: Function, //获取表格数据接口
  //       params: {}, //接口入参
  //     };
  //   },
  // },
  //搜索条件列表
  searchFormList: {
    type: Array,
    default: () => [],
  },
  //表头
  tableColumns: {
    type: Array,
    default: () => [],
  },
  //表格数据
  tableData: {
    type: Array,
    default: () => [],
  },
  //分页
  pagination: {
    type: Object,
    default: undefined,
    // default: () => {
    //   return {
    //     pageSize: 10, //每页显示数
    //     pageNum: 1, //当前页码
    //     total: 0, //数据总量
    //     fullData: false, //是否全量数据返回。为true时候将开启前端分页
    //   };
    // },
  },
});

const data = reactive({
  showColumnSetting: false, //列设置弹窗的显隐
  radio: '', //单选值
  searchValue: '', //搜索内容
  sortColumn: {}, //激活排序的列
  filterCollect: {}, //筛选的列及值
  searchColumns: [], //可被顶部搜索框搜索出来的列（前端分页用）
  showTableData: [], //实际展示的表格
  selectionList: [], //复选框选中的数据
});

const pageable = computed(() => props.pagination || false); //分页器

watch(
  () => props.tableData,
  (val) => {
    if (pageable.value.fullData) {
      //若是有分页且全量数据返回了数据，则做前端分页
      const startIndex = (pageable.value.pageNum - 1) * pageable.value.pageSize; //计算截取的数据的初始位置
      data.showTableData = val.slice(startIndex, startIndex + pageable.value.pageSize);
    } else {
      data.showTableData = val;
    }

    //表头筛选有下拉框的情况
    nextTick(() => {
      getSelectOptions(data.showTableData);
    });
  },
  { deep: true, immediate: true },
);

onMounted(() => {
  dragSort();
  // props.requestApi && getData();
});

/**
 * 获取表格数据
 * */
// const getData = () => {
//   data.loading = true;
//   props.requestTable
//     .api(props.requestTable.params)
//     .then((res) => {
//       data.showTableData = res.data.list;
//       pageable.value.total = res.data.total;
//       pageable.value.fullData = res.data.list.length === res.data.total; //是否全量数据返回
//     })
//     .finally(() => {
//       data.loading = false;
//     });
// };
/**
 * 表头设置确认
 * @columns {array} 动态设置的列
 * */
const confirmSetting = (columns) => {
  // clearFilterAll();
  // pageable.value.pageNum = 1; //重置页码
  getSelectOptions(data.showTableData);
  emit('confirmSetting', columns);
};
/**
 * 获取筛选的下拉选项
 * @list {array} 当前页的表格数据
 */
const getSelectOptions = (list) => {
  data.searchColumns = []; //置空数据，避免push相同数据
  props.tableColumns.map((column) => {
    column.search && data.searchColumns.push(column.prop); //可被顶部搜索框搜索出来的列
    //有筛选
    if (column.filter && column.filter === 'select') {
      //下拉框筛选
      column.filterList = []; //下拉列表
      // column.filterVal = [];
      list.map((item) => {
        column.filterList.push(item[column.prop]);
      });
      column.filterList = Array.from(new Set(column.filterList));
    }
  });
};
/**
 * 排序
 * @column {string} 排序的列的
 * @type {string} ascending升序，descending降序，null清空排序
 * @flag {boolean} 是否是点击进来的
 */
const onSort = (column, type, flag) => {
  //重复点击同一排序
  if (column.order === type && flag) {
    tableRef.value.clearSort();
    column.activeSort = 0;
    return false;
  }

  data.sortColumn.activeSort = 0; //清空上一个设置的排序列
  tableRef.value.clearSort();
  //颜色激活
  type ? (column.activeSort = (type === 'ascending' && 1) || 2) : (column.activeSort = 0);
  tableRef.value.sort(column.property, type); //排序方法
  data.sortColumn = column; //赋值当前列
};
/**
 * 文本框的筛选
 * @column {object} 筛选的列
 * @info {object} 筛选的列的数据
 * @filterType {string} 筛选类型。select。date
 */
const inputFilter = (column, info, filterType) => {
  clearTimeout(timer);
  timer = setTimeout(() => {
    changeFilter(column, info, filterType);
  }, 500);
};
/**
 * 下拉框、时间的筛选
 * @column {object} 筛选的列
 * @info {object} 筛选的列的数据
 * @filterType {string} 筛选类型。input，select。date
 */
const changeFilter = (column, info, filterType) => {
  info.filterVal = column.filteredValue; //选择的值
  data.showTableData = [];

  //筛选的列的值
  data.filterCollect[column.property] = {
    key: column.property,
    value: column.filteredValue,
    filterType,
  };

  let tempList = [props.tableData]; //过滤过程中使用的临时二维数组
  if (pageable.value.fullData) {
    //前端分页时的筛选
    const startIndex = (pageable.value.pageNum - 1) * pageable.value.pageSize;
    let list = props.tableData.slice(startIndex, startIndex + pageable.value.pageSize); //当前页的数据
    tempList = [list]; //过滤过程中使用的临时二维数组
  }
  filterRes(tempList);

  //若有排序需保持排序
  nextTick(() => {
    column.order && onSort(column, column.order);
    data.sortColumn.order && onSort(data.sortColumn, data.sortColumn.order);
  });
};
/**
 * 筛选的副方法
 * @tempList {array} 过滤过程中使用的临时二维数组
 */
const filterRes = (tempList) => {
  let filterCollect = Object.values(data.filterCollect); //对象的值转数组
  filterCollect.map((filter, index) => {
    tempList[index + 1] = []; //下一轮过滤使用的数组

    tempList[index].map((item) => {
      //filter.key为列属性；filter.value为筛选输入、选择的值；filter.filterType筛选类型
      let flag = true; //数据是否展示
      switch (filter.filterType) {
        case 'input':
          var columnsValue = item[filter.key] + ''; //该列各行的值，统一转为字符串
          columnsValue.length && (flag = columnsValue.includes(filter.value)); //有筛选值才做判断
          break;
        case 'select':
          filter.value.length && (flag = filter.value.includes(item[filter.key])); //有筛选值才做判断
          break;
        case 'date':
          //有筛选值才做判断
          if (filter.value) {
            var startTime = new Date(filter.value[0]).getTime(); //开始时间，00:00:00
            var endTime = filter.value[1]; //结束时间，23:59:59
            // var endTime = new Date(filter.value[1]).getTime(); //结束时间,23:59:59
            var dataDate = new Date(item[filter.key]).getTime();

            //结束时间到结束日期的23:59:59
            endTime = new Date(
              endTime.getFullYear(),
              endTime.getMonth(),
              endTime.getDate(),
              23,
              59,
              59,
            ).getTime();
            flag = startTime <= dataDate && endTime >= dataDate;
          }
          break;
      }
      flag && tempList[index + 1].push(item); //将当前轮过滤到的数据存放到下一轮的数组里
    });
  });
  data.showTableData = tempList[tempList.length - 1]; //显示过滤后的数据
};
/**
 * 清空所有的筛选及排序
 */
const clearFilterAll = () => {
  //清空排序
  tableRef.value.clearSort();
  data.sortColumn.activeSort = 0;
  data.sortColumn = {}; //激活排序的列

  //清空筛选
  data.filterCollect = {}; //筛选的列及值
  props.tableColumns.map((column) => {
    column.filterVal = '';
    column.filteredValue = [];
  });
};
/**
 * 搜索表格
 * @value {string} 搜索内容
 */
const searchTable = (value) => {
  clearFilterAll();
  //前端分页情况
  if (pageable.value.fullData) {
    data.showTableData = [];
    searchTableData = (value && searchData(value)) || props.tableData;

    //展示搜索到的数据
    pageable.value.pageNum = 1; //重置页码
    pageable.value.total = searchTableData.length; //设置数据总量
    const startIndex = (pageable.value.pageNum - 1) * pageable.value.pageSize; //要截取的数据的初始下标
    data.showTableData = searchTableData.slice(startIndex, startIndex + pageable.value.pageSize);
  } else {
    emit('searchTable', data.searchValue); //非前端分页的搜索需要调取接口
  }

  /**
   * 递归搜索
   * @keyword {string} 搜索内容
   */
  function searchData(keyword) {
    const loop = (list) => {
      const result = [];

      list.map((item) => {
        //根据需要搜索的列
        for (let prop of data.searchColumns) {
          //匹配到搜索的数据
          if (item[prop].includes(keyword)) {
            result.push({ ...item });
            break;
          }
        }

        //若有子节点
        if (item.children) {
          const filterData = loop(item.children);
          if (filterData.length) {
            result.push({
              ...item,
              children: filterData,
            });
          }
        }
      });
      return result;
    };

    return loop(props.tableData);
  }
};
/**
 * page-size 改变时触发
 * @val {number} 每页显示数
 */
const sizeChange = (val) => {
  //全量数据返回，前端分页
  if (pageable.value.fullData) {
    let dataList = (searchTableData.length && searchTableData) || props.tableData; //有用框搜索数据
    data.showTableData = dataList.slice(0, val);
    getSelectOptions(data.showTableData);
  }

  pageable.value.pageSize = val;
  pageable.value.pageNum = 1; //改变每页显示数后，重置页码
  emit('pageChange', 1, pageable.value.pageSize); //当前页，每页显示数
  clearFilterAll();
};
/**
 * current-page 改变时触发
 * @val {number} 当前页码
 */
const currentChange = (val) => {
  //全量数据返回，前端分页
  if (pageable.value.fullData) {
    const startIndex = (val - 1) * pageable.value.pageSize; //计算截取的数据的初始位置
    let dataList = (searchTableData.length && searchTableData) || props.tableData; //有用框搜索数据
    //截取对应页码的数据
    data.showTableData = dataList.slice(startIndex, startIndex + pageable.value.pageSize);
    getSelectOptions(data.showTableData);
  }

  pageable.value.pageNum = val;
  emit('pageChange', val, pageable.value.pageSize); //当前页，每页显示数
  clearFilterAll();
};
/**
 * 拖拽排序
 */
const dragSort = () => {
  const tbody = tableRef.value.$el.querySelector('.el-table__body-wrapper tbody');
  Sortable.create(tbody, {
    animation: 200, // 拖动时的元素的位置变化的动画时长，
    handle: '.move',
    onEnd({ newIndex, oldIndex }) {
      // 首先删除原来的那一项，并且保存一份原来的那一项，因为splice返回的是一个数组，数组中的第一项就是删掉的那一项
      const currRow = data.showTableData.splice(oldIndex, 1)[0];
      // 然后把这一项加入到新位置上
      data.showTableData.splice(newIndex, 0, currRow);
      emit('dragSort', { newIndex, oldIndex });
    },
  });
};
/**
 * 获取复选框选中项
 * @list {array} 勾选的数据
 */
const handleSelectionChange = (list) => {
  data.selectionList = list;
  emit('selectionChange', list);
};

const emit = defineEmits([
  'onSearch',
  'selectionChange',
  'radioChange',
  'searchTable',
  'pageChange',
  'confirmSetting',
  'dragSort',
]);

// 暴露给父组件的参数和方法 (外部需要什么，都可以从这里暴露出去)
defineExpose({
  element: tableRef,
});
</script>

<style scoped lang="less">
.pi-table {
  display: flex;
  flex: 1;
  flex-direction: column;
  width: 100%;
  .table-main {
    display: flex;
    flex: 1;
    flex-direction: column;
  }
  .el-table {
    flex: 1;
  }
  :deep(.el-table tr th) {
    background: var(--el-fill-color-light);
    color: var(--el-text-color-primary);
    .cell {
      display: flex;
      align-items: center;
    }
    .header-cell {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
  :deep(.el-table__column-filter-trigger) {
    display: none;
  }
  .filter-icon {
    float: right;
    margin-top: 6px;
  }
  .table-header {
    display: flex;
    justify-content: space-between;
    :deep(.el-button) {
      margin-bottom: 14px;
    }
    .el-input {
      width: 220px;
      margin-bottom: 14px;
    }
  }
  .move {
    cursor: move;
    span i {
      cursor: move;
    }
  }
}
:deep(.el-scrollbar__view) {
  height: 100%;
}
.table-filter-box {
  user-select: none;
  .filter-top {
    padding: 6px 10px 10px 10px;
    border-bottom: 1px solid #ddd;
    .sub-title {
      color: #999;
      margin-bottom: 10px;
    }
    .block-btn {
      display: block;
      margin-bottom: 4px;
    }
  }
  .filter-bottom {
    padding: 6px 10px;
    .sub-section {
      margin-bottom: 10px;
    }
  }
  .sub-title {
    color: #999;
    margin-bottom: 10px;
    .title-btn {
      vertical-align: top;
      float: right;
      font-size: 12px;
    }
  }
}
:deep(.column-radio .el-radio__label) {
  display: none;
}
.active-color {
  color: var(--el-color-primary);
}
</style>
